/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.com.cpqd.ssar.gerenciador;

import br.com.cpqd.ssar.mediador.RequisicaoServicos;
import br.com.cpqd.ssar.utilitarias.AtributosGeometria;
import br.com.cpqd.ssar.utilitarias.Cilindro;
import br.com.cpqd.ssar.utilitarias.Cone;
import br.com.cpqd.ssar.utilitarias.Esfera;
import br.com.cpqd.ssar.utilitarias.ExportadorSTL;
import br.com.cpqd.ssar.utilitarias.FonteEletromagneticaSimulacao;
import br.com.cpqd.ssar.utilitarias.Geometria;
import br.com.cpqd.ssar.utilitarias.ListaGeometrias;
import br.com.cpqd.ssar.utilitarias.Material;
import br.com.cpqd.ssar.utilitarias.STL;
import br.com.cpqd.ssar.utilitarias.Tijolo;
import br.com.cpqd.ssar.utilitarias.Triangulo;
import br.com.cpqd.ssar.utilitarias.Vertice;
import java.awt.Color;
import java.util.List;
import java.util.Timer;
import java.util.Vector;

/**
 * Classe que gerencia os estados e processos para a realiza��o de uma simula��o
 * completa.
 * @author Igor Freitas
 * @version 1.0
 */
public class GerenciamentoNucleo {

    /* status da simulacao
     * 0 software iniciado sem nenhuma acao do usuario ou sem Geometrias
     * 1 geometria inserida e/ou stl e/ou dxf criados
     * 2 geometrias e/ou STL e/ou DXF com materiais setados
     * 3 Triangularizacao efetuada
     * 4 malha feita sobre geometrias, dxf ou stl
     * 5 definicao da fonte de radiacao
     * 6 definicao dos materiais
     * 7 escolha de tipos de relatorios efetuada
     * 8 simulacao sendo efetuada
     * 9 simulacao terminada
     * 10 relatorios gerados e instanciados
     */
    //private Status status = new Status();
    private List rootGeometrias = new Vector(); //lista de objetos ListaGeometrias
    private List rootRelatorios = new Vector();
    private List listGeo; //lista de objetos Geometry
    private GerenciamentoArvore managementTree = new GerenciamentoArvore();
    private ListaGeometrias listaGeometrias; //lista de Objetos Geometry
    private int idList = 0;
    private int idGeometria = 0;
    private RequisicaoServicos interfaceServicos;
    /* Vetor de Vertice[] que contem Maximos e Minimos da Cena de Simulacao */
    private Vertice[] vertexMaxMinCena = new Vertice[2];
    private static Geometria dominioComputacional;
    private Geometria copiaGeometria = null;
    private Geometria referenciaGeometriaCopiada = null;
    private float rotacao = 1.0f;
    private float translacao = 1.0f; 
    private int statusFDTD = 0;

    /**
     * 
     * @param interfaceServicos - RequisicaoServicos
     */
    public GerenciamentoNucleo(RequisicaoServicos interfaceServicos) {
        this.interfaceServicos = interfaceServicos;

    }
    
    public int getStatusFDTD(){
        return statusFDTD;
    }
    
    public void setStatusFDTD(int status){
        statusFDTD = status;
    }


    /**
     * Método que retorna o controlador "controladorServicos" no qual é usado 
     * para acessar todos os servicos providos pelos outros métodos como 'ImportarSTL',
     * 'gerarMalha', 'simularFDTD3D' entre outros.
     * @return
     */
    public RequisicaoServicos getInterfaceServicos() {
        return this.interfaceServicos;
    }

    /**
     * Método que seta domínio computacional da cena de simulação
     * @param dominio - Geometria
     */
    public void setDominioComputacional(Geometria dominio) {
        dominioComputacional = dominio;
    }
    
    public Timer getTimer(){
        return new Timer();
    }

    /**
     * Método que recebe uma Geometria e faz uma cópia para um novo objeto
     * Geometria, este novo objeto é unico no 'GerenciamentoNucleo'
     * @param obj - Geometria
     */
    public Geometria copiarGeometria(Geometria obj) {

        try {
            /* Primeiro é feita uma copia dos atributos da Classe Geometria */
            /* Este parâmetro é alterado quando o usuário colar o objeto */
            int identificador = obj.getIdentificador();
            String nome = obj.getNome();
            boolean visivel = obj.isVisible();
            boolean solido = obj.isSolid();
//            float xMin = obj.getXMin();
//            float xMax = obj.getXMax();
//            float yMin = obj.getYMin();
//            float yMax = obj.getYMax();
//            float zMin = obj.getZMin();
//            float zMax = obj.getZMax();
            float rotX = obj.getRotX();
            float rotY = obj.getRotY();
            float rotZ = obj.getRotZ();

            /* Cloando os Atributos */
            AtributosGeometria atributos = null;

            float[] referencial = new float[3];
            referencial[0] = obj.getAtributos().getReferencial(0);
            referencial[1] = obj.getAtributos().getReferencial(1);
            referencial[2] = obj.getAtributos().getReferencial(2);
            int identificadorAtributos = obj.getAtributos().getIdentificador();
            float[] normal = new float[3];
            normal[0] = obj.getAtributos().getNormal()[0];
            normal[1] = obj.getAtributos().getNormal()[1];
            normal[2] = obj.getAtributos().getNormal()[2];
            //atributos = new AtributosGeometria(referencial, identificadorAtributos);   
            if (obj.getAtributos() instanceof Tijolo) {

                float[] firstPoint = new float[3];
                firstPoint[0] = obj.getAtributos().getFirsPoint()[0];
                firstPoint[1] = obj.getAtributos().getFirsPoint()[1];
                firstPoint[2] = obj.getAtributos().getFirsPoint()[2];

                float[] secondPoint = new float[3];
                secondPoint[0] = obj.getAtributos().getSecondPoint()[0];
                secondPoint[1] = obj.getAtributos().getSecondPoint()[1];
                secondPoint[2] = obj.getAtributos().getSecondPoint()[2];
                atributos = new Tijolo(firstPoint, secondPoint, referencial, identificadorAtributos);

            }
            if (obj.getAtributos() instanceof Esfera) {

                float radius = obj.getAtributos().getRaio();
                atributos = new Esfera(radius, referencial, identificadorAtributos);
            }
            if (obj.getAtributos() instanceof Cone) {

                float height = obj.getAtributos().getAltura();
                float radius = obj.getAtributos().getRaio();
                atributos = new Cone(radius, height, referencial, identificadorAtributos);
            }
            if (obj.getAtributos() instanceof Cilindro) {

                float height = obj.getAtributos().getAltura();
                float radius = obj.getAtributos().getRaio();
                atributos = new Cilindro(radius, height, referencial, identificadorAtributos);
            }

            if (obj.getAtributos() instanceof STL) {
                /* Clonando a Lista de Triangulos */
                Triangulo[] listaTriangulosSTL = new Triangulo[obj.getListaTriangulos().length]; //contem um vertice e normalTriangle

                for (int i = 0; i < obj.getListaTriangulos().length; i++) {
                    listaTriangulosSTL[i] = new Triangulo();
                    listaTriangulosSTL[i].setup(obj.getListaTriangulos(i));
                }

                float[] firstPoint = new float[3];
                firstPoint[0] = obj.getAtributos().getFirsPoint()[0];
                firstPoint[1] = obj.getAtributos().getFirsPoint()[1];
                firstPoint[2] = obj.getAtributos().getFirsPoint()[2];

                float[] secondPoint = new float[3];
                secondPoint[0] = obj.getAtributos().getSecondPoint()[0];
                secondPoint[1] = obj.getAtributos().getSecondPoint()[1];
                secondPoint[2] = obj.getAtributos().getSecondPoint()[2];

                String caminho = obj.getAtributos().getCaminhoArquivo();
                atributos = new STL(listaTriangulosSTL, caminho, identificadorAtributos, firstPoint, secondPoint, obj.getAtributos().getNElements());
            }

            /* Clonando os Materiais */

            int id = obj.getMaterial().getId();
            boolean removivel = obj.getMaterial().isRemovivel();
            double permissividade = obj.getMaterial().getPermissividade();
            double permeabilidade = obj.getMaterial().getPermeabilidade();
            double condutividade = obj.getMaterial().getCondutividade();
            double perdaMagnetica = obj.getMaterial().getPerdaMagnetica();
            double densidadeEspecifica = obj.getMaterial().getDensidadeEspecifica();
            String nomeMaterial = obj.getMaterial().getNome();
            Color cor = obj.getMaterial().getCor();
            Material material = new Material(removivel);
            material.setCondutividade(condutividade);
            material.setCor(cor);
            material.setDensidadeEspecifica(densidadeEspecifica);
            material.setId(id);
            material.setNome(nomeMaterial);
            material.setPerdaMagnetica(perdaMagnetica);
            material.setPermeabilidade(permeabilidade);
            material.setPermissividade(permissividade);

            /* Clonando a Lista de Triangulos */
            Triangulo[] listaTriangulos = new Triangulo[obj.getListaTriangulos().length]; //contem um vertice e normalTriangle
            Vertice[] vertice = new Vertice[3]; //contem coordenada

            float[] normalTriangle = new float[3];

            for (int i = 0; i < obj.getListaTriangulos().length; i++) {

                for (int j = 0; j < 3; j++) {

                    float[] coordenada = new float[3];
                    coordenada[0] = obj.getListaTriangulos(i).getVertice(j).getCoordenada(0);
                    coordenada[1] = obj.getListaTriangulos(i).getVertice(j).getCoordenada(1);
                    coordenada[2] = obj.getListaTriangulos(i).getVertice(j).getCoordenada(2);
                    vertice[j] = new Vertice(coordenada);

                }
                normalTriangle[0] = obj.getListaTriangulos(i).getNormal(0);
                normalTriangle[1] = obj.getListaTriangulos(i).getNormal(1);
                normalTriangle[2] = obj.getListaTriangulos(i).getNormal(2);
                listaTriangulos[i] = new Triangulo(vertice, normalTriangle);

            }

            /* Criando a nova Geometria */
            Geometria g = new Geometria(identificador, nome, atributos, visivel, solido, material, rotX, rotY, rotZ);
            g.setListaTriangulos(g.getAtributos().triangularizarGeometria());
            return g;

        } catch (Exception e) {

            System.out.println("erro");
            return null;
        }

    }

    /**
     * Método que adiciona na ListaGeometrias previamente selecionada a instância
     * 'copiaGeometria' que foi copiada da Árvore.
     */
    public boolean colarGeometria(Geometria obj) {

        /* O id da Geometria deve ser único, portanto, ele é alterado somente
         * quando o usuário decide colar o objeto. */
        obj.setIdentificador(idGeometria + 1);
        if (obj.getAtributos() instanceof Tijolo) {
            int id = idGeometria + 1;
            obj.setNome("CopiaTijolo[" + id + "]");
        }
        if (obj.getAtributos() instanceof Cone) {
            int id = idGeometria + 1;
            obj.setNome("CopiaCone[" + id + "]");
        }
        if (obj.getAtributos() instanceof Cilindro) {
            int id = idGeometria + 1;
            obj.setNome("CopiaCilindro[" + id + "]");
        }
        if (obj.getAtributos() instanceof Esfera) {
            int id = idGeometria + 1;
            obj.setNome("CopiaEsfera[" + id + "]");
        }
        if (obj.getAtributos() instanceof STL) {
            int id = idGeometria + 1;
            obj.setNome("CopiaSTL[" + id + "]");
        }
        idGeometria++;
        managementTree.addGeometria(obj);
        //managementTree.updateTree();
        return true;
    }

    /**
     * Método que retorna 'false' caso nenhum objeto foi previamente copiado.
     * @return
     */
    public boolean isColar() {
        if (copiaGeometria == null) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * Método que retorna domínio computacional da cena de simulação
     * @return dominio - Geometria
     */
    public Geometria getDominioComputacional() {
        return dominioComputacional;
    }

    /**
     * Método que retorna false se o objeto recebido, que pode ser uma 
     * FonteEletromagneticaSimulacao ou Geometria, estiver além dos limites 
     * da simulação.
     * @param obj
     * @return status - boolean
     */
    public boolean verificaLimiteComputacional(Object obj) {
        boolean status = true;

        if (obj instanceof FonteEletromagneticaSimulacao) {

            FonteEletromagneticaSimulacao f = (FonteEletromagneticaSimulacao) obj;
            if ((f.getX() < dominioComputacional.getAtributos().getFirsPoint()[0]) ||
                    (f.getX() > dominioComputacional.getAtributos().getSecondPoint()[0]) ||
                    (f.getY() < dominioComputacional.getAtributos().getFirsPoint()[1]) ||
                    (f.getY() > dominioComputacional.getAtributos().getSecondPoint()[1]) ||
                    (f.getZ() < dominioComputacional.getAtributos().getFirsPoint()[2]) ||
                    (f.getZ() > dominioComputacional.getAtributos().getSecondPoint()[1])) {
                status = false;
            }

        }

        if (obj instanceof Geometria) {

            Geometria g = (Geometria) obj;
            /* Triangulariza Geometria */
            g.setListaTriangulos(g.getAtributos().triangularizarGeometria());
            /* Compara com dominio computacional cada vértice de cada triangulo */
            for (int i = 0; i < g.getListaTriangulos().length; i++) {

                Triangulo t = (Triangulo) g.getListaTriangulos(i);
                for (int j = 0; j < 3; j++) {

                    if ((t.getVertice(j).getCoordenada(0) < dominioComputacional.getAtributos().getFirsPoint()[0]) ||
                            (t.getVertice(j).getCoordenada(0) > dominioComputacional.getAtributos().getSecondPoint()[0]) ||
                            (t.getVertice(j).getCoordenada(1) < dominioComputacional.getAtributos().getFirsPoint()[1]) ||
                            (t.getVertice(j).getCoordenada(1) > dominioComputacional.getAtributos().getSecondPoint()[1]) ||
                            (t.getVertice(j).getCoordenada(2) < dominioComputacional.getAtributos().getFirsPoint()[2]) ||
                            (t.getVertice(j).getCoordenada(2) > dominioComputacional.getAtributos().getSecondPoint()[1])) {

                        status = false;
                        break;
                    }
                }
            }
        }
        return status;
    }

   
    /**
     * 
     * @param obj
     * @param dominio
     * @return
     */
    public boolean verificaLimiteComputacional(Object obj, Geometria dominio) {
        boolean status = true;

        if (obj instanceof FonteEletromagneticaSimulacao) {

            FonteEletromagneticaSimulacao f = (FonteEletromagneticaSimulacao) obj;
            if ((f.getX() < dominio.getAtributos().getFirsPoint()[0]) ||
                    (f.getX() > dominio.getAtributos().getSecondPoint()[0]) ||
                    (f.getY() < dominio.getAtributos().getFirsPoint()[1]) ||
                    (f.getY() > dominio.getAtributos().getSecondPoint()[1]) ||
                    (f.getZ() < dominio.getAtributos().getFirsPoint()[2]) ||
                    (f.getZ() > dominio.getAtributos().getSecondPoint()[1])) {
                status = false;
            }

        }

        if (obj instanceof Geometria) {

            Geometria g = (Geometria) obj;
            /* Triangulariza Geometria */
            g.setListaTriangulos(g.getAtributos().triangularizarGeometria());
            /* Compara com dominio computacional cada vértice de cada triangulo */
            for (int i = 0; i <
                    g.getListaTriangulos().length; i++) {

                Triangulo t = (Triangulo) g.getListaTriangulos(i);

                for (int j = 0; j <
                        3; j++) {

                    if ((t.getVertice(j).getCoordenada(0) < dominio.getAtributos().getFirsPoint()[0]) ||
                            (t.getVertice(j).getCoordenada(0) > dominio.getAtributos().getSecondPoint()[0]) ||
                            (t.getVertice(j).getCoordenada(1) < dominio.getAtributos().getFirsPoint()[1]) ||
                            (t.getVertice(j).getCoordenada(1) > dominio.getAtributos().getSecondPoint()[1]) ||
                            (t.getVertice(j).getCoordenada(2) < dominio.getAtributos().getFirsPoint()[2]) ||
                            (t.getVertice(j).getCoordenada(2) > dominio.getAtributos().getSecondPoint()[1])) {

                        status = false;
                        break;

                    }
                }
            }
        }
        return status;


    }

    /**
     * Método que verifica alteração no domínio computacional, retorna verdadeiro
     * caso o domínio não tenha limite menor que as Geometrias e/ou Fontes 
     * Eletromagnéticas contidas na cena de simulação.
     * @param dominio
     * @return boolean = status
     */
    public boolean verificaModificaoDominioComp(Geometria dominio) {
        boolean status = true;

        /* Comparar domínio computacional modificado com a Lista de Geometrias */
        Geometria g;

        for (int i = 0; i <
                getManagementTree().getTodasGeometrias().length; i++) {

            g = (Geometria) getManagementTree().getTodasGeometrias()[i];
            if (!verificaLimiteComputacional(g, dominio)) {
                status = false;
            }

        }

        /* Comparar domínio computacional modificado com a Lista de Fontes */

        FonteEletromagneticaSimulacao f;
        for (int j = 0; j <
                getManagementTree().getListaFontes().size(); j++) {
            f = (FonteEletromagneticaSimulacao) getManagementTree().getListaFontes().get(j);
            if (!verificaLimiteComputacional(f, dominio)) {
                status = false;
            }

        }
        return status;
    }

    /**
     * Seta a árvore que contém a estrutura da simulação
     * @param tree
     */
    public void setTree(GerenciamentoArvore tree) {
        managementTree = tree;
    }

    /**
     * Retorna a árvore que contém a estrutura da simulação
     * @return
     */
    public GerenciamentoArvore getManagementTree() {
        return managementTree;
    }

    /** 
     * cria uma ListaGeometrias que contem uma lista de Geometry vazia
     * este metodo é usado quando o usuario quer criar nova geometria
     */
    public void novaListaGeometrias() {
        listGeo = new Vector();
        idList++;

        listaGeometrias =
                new ListaGeometrias(listGeo, idList);
        rootGeometrias.add(listaGeometrias);
        managementTree.addListaGeometria(listaGeometrias);
        managementTree.updateTree();

    }

    /**   
     * Metodo usado quando o Usuario abre um novo projeto reconstruindo a estrutura
     * da árvore de Geometrias
     * @param listGeo
     */
    public void novaListaGeometrias(Vector listGeo) {

        ListaGeometrias lista;
//        status.setGeometriaInserida(false);

        for (int cont = 0; cont <
                listGeo.size(); cont++) {
            lista = new ListaGeometrias();

            lista =
                    (ListaGeometrias) listGeo.get(cont);

            idList++;

            listaGeometrias =
                    new ListaGeometrias(lista.getGeo(), lista.getId(),
                    lista.getName(), lista.isVisible(), lista.isSolid());

            rootGeometrias.add(listaGeometrias);
            managementTree.addListaGeometria(listaGeometrias);
            managementTree.getTree().expandRow(2);
            managementTree.updateTree();

//            if (!listaGeometrias.getGeo().isEmpty()) {
//                status.setGeometriaInserida(true);
//            }



        }

    }

    /**
     * Metodo usado para reconstrução da estrutura da árvore de Fontes Eletromagnéticas
     * de Simulação, neste caso, é usado quando o Usuário abre um projeto existete
     * @param vFontes
     */
    public void carregarListaFontes(Vector vFontes) {

        FonteEletromagneticaSimulacao f;
        for (int i = 0; i <
                vFontes.size(); i++) {
            f = (FonteEletromagneticaSimulacao) vFontes.get(i);
            managementTree.addFonte(f);
        }

        getManagementTree().getTree().expandRow(1);


    }

    public void exportarGeometriaParaStl(Geometria geo) {
        geo.setListaTriangulos(geo.getAtributos().triangularizarGeometria());
        ExportadorSTL criarSTL = new ExportadorSTL(geo.getListaTriangulos(), "D:/" + geo.getNome());
    }

    /**
     * Método que retorna objeto STL
     * @return Geometria
     */
    public Geometria novoSTL() {

        /* recebe o STL da interface de servicos */
        try {

            Geometria stl = interfaceServicos.importarSTL();//getManagementTree().getTodasGeometrias().length+1);
            /* Cria Material e Cor padrão */
            Material m = new Material(true);
            Color cor = new Color(125, 0, 0);
            m.setCor(cor);
            m.setId(0);
            stl.setMaterial(m);
            /* Define Referencial pra o STL - padrao (0,0,0) */


            float[] reference = new float[3];
            reference[0] = 0;
            reference[1] = 0;
            reference[2] = 0;
            stl.getAtributos().setReferencial(reference);
            stl.setIdentificador(getIdentificadorGeometria());
            stl.setNome("STL [" + stl.getIdentificador() + "]");

            stl.setVisible(true);
            stl.setSolid(
                    true);

//            if (!managementTree.getListaGeometrias().isEmpty()) {
//                status.setGeometriaInserida(true);
//            } else {
//                status.setGeometriaInserida(false);
//            }
            return stl;

        } catch (Exception e) {
            System.out.println("STL não selecionado");
            return null;
        }

    }

    /**
     * Método que controla os Identificadores das Geometrias inseridas
     * @return idGeometria
     */
    public int getIdentificadorGeometria() {
        this.idGeometria++;
        return idGeometria;
    }

    /**
     * Retorna Vertice que contem os maximos e minimos da cena de simulacao
     * @return vertexMaxMinCena Vertice[]
     */
    public Vertice[] getMaxMinCena() {
        return vertexMaxMinCena;
    }

    /**
     * Triangulariza todas as Geometrias Inseridas e seta 'status' da simulacao
     * Status = 2 => Geometrias triangularizadas (maximos e minimos definidos)
     * @return status int
     */
    public void triangularizarGeometrias() {

        Geometria[] t = getManagementTree().getTodasGeometrias();
        Geometria g;
        /* Fazendo a varredura em cada Lista de Geometrias */

        for (int i = 0; i <
                t.length; i++) {

            g = (Geometria) t[i];
            /* Triangulizarando Geometria */
            //g.setListaTriangulos(g.getAtributos().triangularizarGeometria());
            g.calculaMaxMin();

        }
    }

    public void setRotacao(float r) {
        rotacao = r;
    }

    public float getRotacao() {
        return rotacao;
    }
    
    public void setTranslacao(float t) {
        translacao = t;
    }

    public float getTranslacao() {
        return translacao;
    }
}


